﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
//using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections;
using log4net;
using RollBack.src.ui.ExplorerView;
using RollBackLib.src.utils;
using RollBackLib.src.core;


namespace RollBack.src.ui
{
    /// <summary>
    /// The filelist view of the right side of the explorer window
    /// </summary>
    public partial class ExplorerRightPanel : UserControl
    {
        /// <summary>
        /// Current mode of the panel
        /// </summary>
        public enum ViewMode
        {
            BACKUP_MODE,
            RESTORE_MODE
        }
        private ViewMode m_panelViewMode;

        public ExplorerData m_explorerViewData
        {
            get;
            private set;
        }

        public ExplorerRightPanel( )
        {
            InitializeComponent();
            this.m_fileListView.LargeImageList = ImageResources.m_resources.m_imageList;
            this.m_fileListView.SmallImageList = ImageResources.m_resources.m_imageList;
            m_backupList = new List<ListViewItem>();
            m_restoreList = new List<ListViewItem>();
            m_panelViewMode = ViewMode.BACKUP_MODE;
            this.ContextMenu = new ContextMenu();
        }

        #region DragAndDropFunctions
        private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            if (m_explorerViewData.m_rightData.m_allowDrag)
            {
                log.Debug("Dragging " + e.Item);
                string hashCode = getSelectedItemsInDragNDrop();
                base.DoDragDrop(hashCode, DragDropEffects.Copy);
            }
        }

        // convert the selected items in the file list view to a comma separated list
        private string getSelectedItemsInDragNDrop( )
        {
            ListView.SelectedListViewItemCollection selected = this.m_fileListView.SelectedItems;
            StringBuilder sb = new StringBuilder();
            
            foreach (ListViewItem item in selected)
            {
                sb.Append(", ");
                string p = 
                    m_explorerViewData.m_rightData.getPathOnDisk(m_explorerViewData) +
                    m_explorerViewData.CurrentPath + 
                    "\\" +
                    item.Text;
                sb.Append( p );
            }
            sb.Insert(0, "" + this.m_fileListView.GetHashCode());
            return sb.ToString();
        }

        private void listView1_DragEnter(object sender, DragEventArgs e)
        {
            if (m_explorerViewData.m_rightData.m_allowDrop)
            {
                base.OnDragEnter(e);
                string[] list = GetDragData(sender, e);
                if (list != null)
                {
                    String text = (String)e.Data.GetData(DataFormats.Text);
                    log.Debug("Getting " + text + " from drag enter");
                    e.Effect = DragDropEffects.Copy;
                }
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private string[] GetDragData(object sender, DragEventArgs e)
        {
            string[] list = ParseDragData(e);
            if (list.Length <= 1)
            {
                log.Info("Data length too short");
                return null;
            }
            bool me = IsMyListView(list[0]);
            if (!e.Data.GetDataPresent(DataFormats.Text) || me)
            {
                if (!me) {
                    log.Info("Drag data not in text format");
                }
                e.Effect = DragDropEffects.None;
                return null;
            }
            log.Debug("this=" + this.m_fileListView.GetHashCode() + " sender=" + sender.GetHashCode());
            return list;
        }

        private string[] ParseDragData(DragEventArgs e)
        {
            string[] list = null;
            if (!e.Data.GetDataPresent(DataFormats.Text))
            {
                // data not available or not in string format.
                log.Info("No data present");
                return null;
            }
            string data = (string)e.Data.GetData(DataFormats.Text);
            list = data.Split(',');
            return list;
        }

        // return true if the sender of the drag event happens to be myself.
        private bool IsMyListView(string me)
        {
            int hashCode = Int32.Parse(me);
            if (hashCode == this.m_fileListView.GetHashCode())
            {
                return true;
            }
            return false;
        }

        /*
        private void listView1_DragLeave(object sender, EventArgs e)
        {
            base.OnDragLeave(e);
        }
         * */

        private void listView1_DragDrop(object sender, DragEventArgs e) {
            if (m_explorerViewData.m_rightData.m_allowDrop)
            {
                bool enabled = true;
                string[] list = GetDragData(sender, e);
                if (list != null)
                {
                    String text = (String)e.Data.GetData(DataFormats.Text);
                    log.Info("Getting " + text + " from drag enter this=" + this);
                    e.Effect = DragDropEffects.Copy;
                    for (int i = 1; i < list.Length; i++)
                    {
                        string d = list[i];
                        this.Add(d, true,enabled);
                    }
                }
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Add a file/directory to the file list panel
        /// </summary>
        /// <param name="d"></param>
        /// <param name="p"></param>
        private void Add(string d,bool fullPath,bool enabled)
        {
            if (m_explorerViewData != null)
            {
                ListViewItem item = m_explorerViewData.m_rightData.createItem(d, fullPath);
                Add(item,enabled);
            }
        }

        #endregion

        internal void SetCurrentDir(string dir)
        {
            if (m_explorerViewData != null)
            {
                m_explorerViewData.CurrentPath = dir;
            }
        }


        /// <summary>
        /// Add the supplied list of string to the file list
        /// </summary>
        /// <param name="items"></param>
        public void setListOfItems(List<BackupItem> items)
        {
            foreach (BackupItem item in items)
            {
                this.Add(item.m_path,true,item.m_enabled);
            }
        }

        public void setRestoreList(List<BackupItem> items)
        {
            foreach (BackupItem item in items)
            {
                ListViewItem listViewItem = m_explorerViewData.m_rightData.createItem(item.m_path,true);
                m_restoreList.Add(listViewItem);
                if (item.m_enabled)
                {
                    listViewItem.ForeColor = Color.Black;
                }
                else
                {
                    listViewItem.ForeColor = Color.Gray;
                }
                listViewItem.Checked = item.m_enabled;
            }
        }

        /// <summary>
        /// read the contents of the directory and refresh the file list.
        /// </summary>
        /// <param name="dir"></param>
        internal void expandNode(string dir,bool checkAll,bool force)
        {
            if (m_explorerViewData != null)
            {
                ExplorerData.RightPanelData rdata = m_explorerViewData.m_rightData;
                // refresh only if the 'dir' is not the same as the current directory
                if (string.CompareOrdinal(m_explorerViewData.CurrentPath, dir) != 0 || force)
                {
                    this.SuspendLayout();
                    List<ListViewItem> items = rdata.expandNode(m_explorerViewData, dir, checkAll);
                    m_fileListView.Items.Clear();
                    Add(items);
                    this.ResumeLayout();
                    SetCurrentDir(dir);
                    rdata.m_itemsChecked = checkAll;
                    log.Debug("Finished population of right panel with contents of <" + dir + ">");
                }
                else if(rdata.m_itemsChecked != checkAll)
                {
                    foreach (ListViewItem item in m_fileListView.Items)
                    {
                        item.Checked = checkAll;
                    }
                    rdata.m_itemsChecked = checkAll;
                }
            }
            else
            {
                log.Info("ExplorerViewData is null, skipped population of right panel with contents of <" + dir + ">");
            }
        }

        
        /// <summary>
        /// Switch the list of items in the listview to the backup list
        /// </summary>
        internal void switchToBackupList()
        {
            if (m_panelViewMode != ViewMode.BACKUP_MODE)
            {
                m_panelViewMode = ViewMode.BACKUP_MODE;
                updateListView();
            }
        }

        /// <summary>
        /// Switch the list of items in the listview to the restore list
        /// </summary>
        internal void switchToRestoreList()
        {
            if (m_panelViewMode != ViewMode.RESTORE_MODE)
            {
                m_panelViewMode = ViewMode.RESTORE_MODE;
                updateListView();
            }
        }

        /// <summary>
        /// Based on the current viewing mode update the internal backup/restore list's
        /// </summary>
        internal void updateInternalList( )
        {
            if (m_panelViewMode == ViewMode.RESTORE_MODE)
            {
                // clear the restore list and add the contents of the current list view to the restore list.
                m_restoreList.Clear();
                foreach (ListViewItem item in m_fileListView.Items)
                {
                    m_restoreList.Add(item);
                }
            }
            else if (m_panelViewMode == ViewMode.BACKUP_MODE)
            {
                // clear the backup list and add the contents of the current list view to the backup list.
                m_backupList.Clear();
                foreach (ListViewItem item in m_fileListView.Items)
                {
                    m_backupList.Add(item);
                }
            }
        }

        /// <summary>
        /// Update the ListView from the internal Backup/Restore list
        /// </summary>
        internal void updateListView( )
        {
            m_fileListView.Items.Clear();
            List<ListViewItem> list = new List<ListViewItem>();

            if (m_panelViewMode == ViewMode.RESTORE_MODE)
            {
                list = m_restoreList;
            }
            else if(m_panelViewMode == ViewMode.BACKUP_MODE) 
            {
                list = m_backupList;
            }

            // clear the list view and repopulate it
            m_fileListView.Items.Clear();
            foreach (ListViewItem item in list)
            {
                m_fileListView.Items.Add(item);
            }
        }


        /// <summary>
        /// Remove items at specified indexes
        /// </summary>
        /// <param name="indexes"></param>
        internal void removeItems(ListView.SelectedIndexCollection indexes)
        {
            bool resyncRequired = false;
            for (int i = indexes.Count - 1; i >= 0; i--)
            {
                int at = indexes[i];
                string removedData = m_fileListView.Items[i].Text;

                switch(m_panelViewMode) {
                    case ViewMode.BACKUP_MODE:
                        if (i >= m_backupList.Count)
                        {
                            if (!resyncRequired)
                            {
                                resyncRequired = true;
                                log.Warn("ListView items and internal m_backupList out of sync while removing item at index=" + at + ". Item=" + removedData);
                            }
                        }
                        else
                        {
                            // remove the items from the ListView and internal list.
                            m_fileListView.Items.RemoveAt(indexes[i]);
                            m_backupList.RemoveAt(at);
                        }
                        break;
                    case ViewMode.RESTORE_MODE:
                        if (i >= m_restoreList.Count)
                        {
                            if (!resyncRequired)
                            {
                                resyncRequired = true;
                                log.Warn("ListView items and internal m_backupList out of sync while removing item at index=" + at + ". Item=" + removedData);
                            }
                        }
                        else
                        {
                            // remove the items from the ListView and internal list.
                            m_fileListView.Items.RemoveAt(indexes[i]);
                            m_restoreList.RemoveAt(at);
                        }
                        break;
                }
            }

            if (resyncRequired)
            {
                updateInternalList( );
            }
        }

        internal void setExplorerData(ExplorerData explorerData)
        {
            m_explorerViewData = explorerData;
            this.m_fileListView.CheckBoxes = m_explorerViewData.m_rightData.m_checkBoxes;
        }


        internal void addContextMenuItem(MenuItem item)
        {
            this.ContextMenu.MenuItems.Add(item);
        }
    }
}
